//
// Created by liuml on 2018/10/19.
//


#include "XYFFmpeg.h"

XYFFmpeg::XYFFmpeg(XYPlayStatus *playStatus, XYCallJava *callJava, const char *url) {
    this->playStatus = playStatus;
    this->callJava = callJava;
    this->url = url;
    pthread_mutex_init(&init_mutex, NULL);
    pthread_mutex_init(&seek_mutex, NULL);
}

void *decodeFFMpeg(void *data) {
    XYFFmpeg *xyfFmpeg = (XYFFmpeg *) data;
    xyfFmpeg->decodeFFMpegThread();
    pthread_exit(&xyfFmpeg->decodeThread);
}

void XYFFmpeg::prepared() {

    pthread_create(&decodeThread, NULL, decodeFFMpeg, this);

}

//回调
int avformat_callback(void *ctx) {
    XYFFmpeg *fFmpeg = (XYFFmpeg *) (ctx);
    if (fFmpeg->playStatus->exit) {
        return AVERROR_EOF;//达到文件末尾
    }
    return 0;
}

void XYFFmpeg::decodeFFMpegThread() {

    pthread_mutex_lock(&init_mutex);

    av_register_all();
    avformat_network_init();
    pFormatCtx = avformat_alloc_context();

    //如果是网络 加个回调 避免一直阻塞
    pFormatCtx->interrupt_callback.callback = avformat_callback;
    pFormatCtx->interrupt_callback.opaque = this;

    if (avformat_open_input(&pFormatCtx, url, NULL, NULL) != 0) {
        if (LOG_DEBUG) {
            LOGD("can not open url: %s", url);
            callJava->onCallError(CHILD_THREAD, 1001, "can not open url");
        }
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    } else {
        LOGD("can  open url: %s", url);
    }
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        if (LOG_DEBUG) {
            LOGD("can not find streams from url : %s", url);
            callJava->onCallError(CHILD_THREAD, 1002, "can not find streams from url");
        }
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }

    for (int i = 0; i < pFormatCtx->nb_streams; ++i) {
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {//得到音频流
            if (audio == NULL) {
                audio = new XYAudio(playStatus, pFormatCtx->streams[i]->codecpar->sample_rate,
                                    callJava);
                audio->streamIndex = i;
                audio->codecpar = pFormatCtx->streams[i]->codecpar;

                audio->duration = pFormatCtx->duration / AV_TIME_BASE;//求出总时长

                LOGD("总时长 %d", audio->duration);
                audio->time_base = pFormatCtx->streams[i]->time_base;
                duration = audio->duration;
                //回调采样率
                callJava->onCallPcmSamperate(audio->sample_rate);
            }
        }
    }

    //获取解码器
    AVCodec *dec = avcodec_find_decoder(audio->codecpar->codec_id);
    if (!dec) {
        if (LOG_DEBUG) {
            LOGE("can not find decoder");
            callJava->onCallError(CHILD_THREAD, 1003, "can not find decoder");
        }
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }

    //解码器上下文 分配空间
    audio->avCodecContext = avcodec_alloc_context3(dec);
    if (!audio->avCodecContext) {
        if (LOG_DEBUG) {
            LOGE("can not alloc new decodecctx");
        }
        callJava->onCallError(CHILD_THREAD, 1004, "can not alloc new decodecctx");
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }

    //解码器信息复制到解码器上下文当中
    if (avcodec_parameters_to_context(audio->avCodecContext, audio->codecpar) < 0) {
        if (LOG_DEBUG) {
            LOGE("can not fill decodecctx");
            callJava->onCallError(CHILD_THREAD, 1005, "can not fill decodecctx");
        }
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }
    //打开解码器
    if (avcodec_open2(audio->avCodecContext, dec, 0) != 0) {
        if (LOG_DEBUG) {
            LOGE("cant not open audio strames");
        }
        callJava->onCallError(CHILD_THREAD, 1006, "cant not open audio strames");
        exit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }

    if (callJava != NULL) {
        if (playStatus != NULL && !playStatus->exit) {
            callJava->onCallParpared(CHILD_THREAD);
        } else {
            exit = true;
        }
    }
    pthread_mutex_unlock(&init_mutex);
}

XYFFmpeg::~XYFFmpeg() {

    pthread_mutex_destroy(&init_mutex);
    pthread_mutex_destroy(&seek_mutex);
}

void XYFFmpeg::start() {
    if (audio == NULL) {
        return;
    }
    //播放
    audio->play();
    int count = 0;
    while (playStatus != NULL && !playStatus->exit) {
//        LOGD("开始播放")
        if (playStatus->seek) {
            av_usleep(1000 * 100);
            continue;
        }

        //不会把所有数据放入队列里面,避免程序解码完成进入 判断队列里缓存的数据 会清空队列
        if (audio->queue->getQueueSize() > 40) {
            av_usleep(1000 * 100);//休眠了
            continue;
        }


        AVPacket *avPacket = av_packet_alloc();

        pthread_mutex_lock(&seek_mutex);
        int ret = av_read_frame(pFormatCtx, avPacket);
        pthread_mutex_unlock(&seek_mutex);

        if (ret == 0) {
            if (avPacket->stream_index == audio->streamIndex) {
                count++;
//                    LOGD("解码第 %d帧 ", count);
                //入队
                audio->queue->putAvpacket(avPacket);
            } else {
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
            }
        } else {
            //解码完毕
            av_packet_free(&avPacket);
            av_free(avPacket);
            LOGD("入队完毕")

            //判断队列里缓存的数据 清空
            while (playStatus != NULL && !playStatus->exit) {
                if (audio->queue->getQueueSize() > 0) {
                    av_usleep(1000 * 100);
                    continue;
                } else {
                    playStatus->exit = true;
                    break;
                }
            }
            break;
        }

    }


    //播放完成回调
    if (callJava != NULL) {
        callJava->onCallComplete(CHILD_THREAD);
    }
    //退出
    exit = true;
    if (LOG_DEBUG) {
        LOGD("解码完成");
    }


}

void XYFFmpeg::pause() {
    if (audio != NULL) {
        audio->pause();
    }

}

void XYFFmpeg::resume() {
    if (audio != NULL) {
        audio->resume();
    }
}

void XYFFmpeg::release() {

    LOGD("调用ffmpeg release");
    if (playStatus->exit) {
        return;
    }
    playStatus->exit = true;

    pthread_mutex_lock(&init_mutex);

    //如果没有退出 睡眠1S 强制退出
    int sleepCount = 0;
    while (!exit) {
        if (sleepCount > 1000) {
            exit = true;
        }
        LOGD("wait ffmpeg exit %d", sleepCount);
        sleepCount++;
        av_usleep(1000 * 10);//暂停10毫秒
    }

    if (audio != NULL) {
        audio->release();
        delete (audio);
        audio = NULL;
    }

    if (pFormatCtx != NULL) {
        avformat_close_input(&pFormatCtx);
        avformat_free_context(pFormatCtx);
        pFormatCtx = NULL;
    }

    if (playStatus != NULL) {
        playStatus = NULL;
    }

    if (callJava != NULL) {
        callJava = NULL;
    }

    pthread_mutex_unlock(&init_mutex);

}

void XYFFmpeg::seek(int64_t secds) {

    if (duration <= 0) {
        return;
    }

    if (secds >= 0 && secds <= duration) {
        if (audio != NULL) {
            playStatus->seek = true;
            audio->queue->clearAvpacket();
            audio->clock = 0;
            audio->last_time = 0;
            pthread_mutex_lock(&seek_mutex);


            int64_t rel = secds * AV_TIME_BASE;

            //seek 的时候 清空一下队列里面的buffer
            avcodec_flush_buffers(audio->avCodecContext);

            avformat_seek_file(pFormatCtx, -1, INT64_MIN, rel, INT64_MAX, 0);

            pthread_mutex_unlock(&seek_mutex);
            playStatus->seek = false;

        }
    }
}

void XYFFmpeg::setVolume(int percent) {

    if (audio != NULL) {
        audio->setVolmue(percent);
    }
}

void XYFFmpeg::setMute(int mute) {
    if (audio != NULL) {
        audio->setMute(mute);
    }

}

void XYFFmpeg::setPitch(float pitch) {
    if (audio != NULL) {
        audio->setPitch(pitch);
    }
}

void XYFFmpeg::setSpeed(float speed) {
    if (audio != NULL) {
        audio->setSpeed(speed);
    }
}

int XYFFmpeg::getSampleRate() {
    if (audio != NULL) {
        return audio->avCodecContext->sample_rate;
    }
    return 0;
}

void XYFFmpeg::startStopRecord(bool start) {
    if (audio != NULL) {
        audio->startStopRecord(start);
    }

}

bool XYFFmpeg::catAudioPlay(int start_time, int end_time, bool showPcm) {

    if (audio != NULL) {
        //主要做检测判断
        if (start_time >= 0 && end_time <= duration && start_time < end_time) {

            LOGD("seek start_time %d",start_time);
            audio->isCat = true;
            audio->end_time = end_time;
            audio->showPcm = showPcm;
            seek(start_time);
            return true;
        }else{
            LOGD("time error")
        }
    }
    LOGD("audio is NULL")

    return false;
}









